import os
import logging
import subprocess
import zipfile
from pathlib import Path
import shutil

logger = logging.getLogger(__name__)

def get_last_commit_hash(git_dir):
    """Get the hash of the last commit in the specified git repository.
    
    Args:
        git_dir (Path): Path to the git repository directory.
    
    Returns:
        str: The commit hash of the latest commit.
    
    Raises:
        FileNotFoundError: If the git directory does not exist.
        NotADirectoryError: If the path is not a directory.
        ValueError: If the directory is not a valid git repository.
        RuntimeError: If git command fails to execute.
    """
    # Validate git directory
    logger.debug(f"Validating git directory: {git_dir}")
    if not git_dir.exists():
        logger.error(f"Git directory does not exist: {git_dir}")
        raise FileNotFoundError(f"Git directory does not exist: {git_dir}")
    if not git_dir.is_dir():
        logger.error(f"Path is not a directory: {git_dir}")
        raise NotADirectoryError(f"Path is not a directory: {git_dir}")
        
    # Check if it's a valid git repository
    logger.debug("Checking if directory is a valid git repository")
    if not (git_dir / '.git').exists():
        logger.error(f"Not a valid git repository: {git_dir}")
        raise ValueError(f"Not a valid git repository: {git_dir}")
        
    result = subprocess.run(
        ['git', 'rev-parse', 'HEAD'],
        cwd=git_dir,
        capture_output=True,
        text=True
    )
    
    if result.returncode != 0:
        logger.error(f"Failed to get commit hash: {result.stderr}")
        return None
        
    return result.stdout.strip()

def  create_commit_archive(git_dir):
    """Create a zip archive of the previous commit using git archive command.
    
    Args:
        git_dir (Path): Path to the git repository directory.
    
    Returns:
        Path: Path to the created zip archive.
    
    Raises:
        RuntimeError: If git archive command fails to execute.
    """
    zip_path = git_dir / 'last_commit.zip'
    
    # Use git archive to create zip of previous commit
    result = subprocess.run(
        ['git', 'archive', '--format=zip', '-o', str(zip_path), 'HEAD'],
        cwd=git_dir,
        capture_output=True,
        text=True
    )
    
    if result.returncode != 0:
        logger.error(f"Failed to create archive: {result.stderr}")
        raise RuntimeError(f"Failed to create archive: {result.stderr}")
        
    return zip_path

def extract_archive(zip_path, extract_dir):
    """Extract contents of a zip file to the target directory.
    
    Args:
        zip_path (Path): Path to the zip file to extract.
        extract_dir (Path): Target directory for extraction.
    
    Note:
        If the target directory exists, it will be deleted first.
        The source zip file will be deleted after successful extraction.
    """
    logger.debug(f"Preparing extraction directory: {extract_dir}")
    if extract_dir.exists():
        logger.info(f"Removing existing directory: {extract_dir}")
        shutil.rmtree(extract_dir)
    logger.info(f"Creating new directory: {extract_dir}")
    extract_dir.mkdir(parents=True)
    
    try:
        with zipfile.ZipFile(zip_path, 'r') as zipf:
            zipf.extractall(extract_dir)
        # Delete the zip file after successful extraction
        zip_path.unlink()
    except Exception as e:
        # If extraction fails, ensure zip file remains
        raise RuntimeError(f"Failed to extract archive: {str(e)}")

def run_bcompare(bcompare_path, dir1, dir2):
    """Launch Beyond Compare to compare two directories.
    
    Args:
        bcompare_path (Path): Path to Beyond Compare executable.
        dir1 (Path): First directory to compare (will be shown on the left).
        dir2 (Path): Second directory to compare (will be shown on the right).
    
    Note:
        This function will block until Beyond Compare is closed.
        The directories are swapped to show the previous commit on the left
        and current working directory on the right.
    """
    subprocess.run([str(bcompare_path), str(dir2), str(dir1)])